//SA
//$Id: sa_common.h 175 2009-09-12 17:34:23Z Oleg.Bulychov $
#pragma once
#ifndef SA_COMMON_H
#define SA_COMMON_H

//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
//__________________________________________________________________________________
template<class P, class S, class C>
class SA
{
public:
    typedef P TProblem;
    typedef S TSolution;
    typedef C TConfig;
    typedef typename S::TDirection TDirection;
    typedef typename S::TCostType  TCostType;
protected:
    TSolution current_;
    double t_;

public:
    SA()
    {}

    void init(TProblem const& problem, TConfig const& cfg)
    {
        t_ = cfg.initial_temperature_;
        current_.init(problem, cfg);
    }

    void generate_Random_Solution(TProblem const& problem, TConfig const& cfg, uniform_random& rnd)
    {
        current_.generate_Random(cfg, rnd);
        current_.evaluate(problem);
    }

    template<class U>
    void accept_Solution(TProblem const& problem, U const& u)
    {
        current_.assign(u);
        current_.evaluate(problem);
    }

    void set_Initial_Temperature(TProblem const& problem, TSolution& tmp, TConfig const& cfg,
        uniform_random& rnd, int rank)
    {
        //std::cout << "***init t:" << t_ << ", cost = " << current_.get_Cost() << std::endl;
        if (cfg.heating_rate_ > 1.)
        {
            for(;;)
            {
                int sum = 0;
                for (int i = 0; i < 10; ++i)
                {
                    move(problem, tmp, cfg, rnd);
                    if (is_Acceptable(tmp, rnd))
                    {
                        //std::cout << "*ACCEPTABLE " << tmp.get_Cost() << std::endl;
                        ++sum;
                    }
                }
                //std::cout << "***sum = " << sum << std::endl;
                if (sum >= 8)//:TODO review: 80%
                    break;
                t_ *= cfg.heating_rate_;
            }
        }
        debug_Print_Temperature(cfg, rank);
    }

    void move(TProblem const& problem, TSolution& tmp, TConfig const& cfg, uniform_random& rnd)
    {
        tmp.assign(current_);
        tmp.move(cfg.move_probability_, rnd);
        //if (tmp.changed_)
        //    std::cout << "***move " << std::endl;
        tmp.evaluate(problem);
    }

    void accept(TSolution& tmp)
    {
        current_.assign(tmp);
    }

    bool is_Acceptable(TSolution& tmp, uniform_random& rnd)
    {
        return is_Acceptable(tmp, rnd, meta::int2type<TDirection::direction>());
    }

    bool is_Acceptable(TSolution& tmp, uniform_random& rnd, meta::int2type<OPT_MAXIMIZE>)
    {
        TCostType new_cost = tmp.get_Cost();
        TCostType cur_cost = current_.get_Cost();
        if (new_cost > cur_cost)
            return true;
        return rnd.next_Double() * (1. + exp((cur_cost - new_cost) / t_ )) < 2.;
    }

    bool is_Acceptable(TSolution& tmp, uniform_random& rnd, meta::int2type<OPT_MINIMIZE>)
    {
        TCostType new_cost = tmp.get_Cost();
        TCostType cur_cost = current_.get_Cost();
        if (new_cost < cur_cost)
            return true;
        return rnd.next_Double() * (1. + exp((new_cost - cur_cost) / t_ )) < 2.;
    }

    void update_Temperature(TConfig const& cfg, int step, int rank)
    {
        if ((step % cfg.isotherm_moves_) == 0)
        {
            t_ *= cfg.cooling_rate_;
            debug_Print_Temperature(cfg, rank);
        }
    }

    void debug_Print_Temperature(TConfig const& cfg, int rank)
    {
        if (rank >= cfg.debug_level_)
            return;
        std::cout << std::setw(2) << rank << ": new t=" << t_ << std::endl;
    }

    void update_Solution(TSolution& sol, TConfig const& cfg, int step, int rank)
    {
        TCostType cost = current_.get_Cost();
        if (TDirection()(cost, sol.get_Cost()))
        {
            sol.assign(current_);
        }
        if (rank >= cfg.debug_level_)
            return;

        static const TCostType bad = ((int)TDirection::direction == OPT_MAXIMIZE) ?
            std::numeric_limits<TCostType>::min() : std::numeric_limits<TCostType>::max();
        static TCostType prev = bad;
        if (prev != bad)
        {
            if (TDirection()(prev, cost))
            {
                std::cout << std::setw(2) << rank << ":" << std::setw(5) << step << "<<<WORSE: " << cost <<
                    " -" << abs(cost - prev) << std::endl;
            }
            else if (TDirection()(cost, prev))
            {
                std::cout << std::setw(2) << rank << ":" << std::setw(5) << step << ">>>BETTER: " << cost <<
                    " +" << abs(cost - prev) << std::endl;
                prev = cost;
            }
            else
            {
                std::cout << std::setw(2) << rank << ":" << std::setw(5) << step << "===SAME: " << cost << std::endl;
            }
        }
        else
            prev = cost;
    }

    template<class V>
    void export_Solution(V& v)
    {
        v << current_;
    }
};
//__________________________________________________________________________________
template<class Recv, class Send, class ISend>
class ring_cooperation
{
public:
    template<class SA, class TSolution, class TConfig, class Comm>
    static bool cooperate(SA& sa, int rank, int prev, int next, int num_proc, TSolution& tmp,
        TConfig const& cfg, int step, Comm& comm)
    {
        if (rank == prev || !cfg.cooperation_rate_)
            return false;

        Recv in(prev, comm);
        if (cfg.async_mode_)
        {
            ISend out(next, comm);
            return cooperate_Async(sa, rank, num_proc, tmp, cfg, step, in, out);
        }
        Send out(next, comm);
        return cooperate_Sync(sa, rank, num_proc, tmp, cfg, step, in, out);
    }

    template<class SA, class TSolution, class TConfig>
    static bool cooperate_Async(SA& sa, int rank, int num_proc, TSolution& tmp,
        TConfig const& cfg, int step, Recv& in, ISend& out)
    {
        if ((step % cfg.cooperation_rate_) == 0)
        {
            if (0 == rank)
            {//:start the 'stadium' wave
                sa.export_Solution(out);
            }
        }
        if (!in.is_Data_Available())
            return false;
        //std::cout << rank << ":" << step << ":***migrant accepted" << std::endl;
        in >> tmp;
        if (0 != rank)
        {//:continue wave
            sa.export_Solution(out);
        }
        return true;
    }

    template<class SA, class TSolution, class TConfig>
    static bool cooperate_Sync(SA& sa, int rank, int num_proc, TSolution& tmp,
        TConfig const& cfg, int step, Recv& in, Send& out)
    {
        if (step % cfg.cooperation_rate_)
            return false;
        if (num_proc & 1)
        {//:odd - slow
            if (0 == rank)
                sa.export_Solution(out);

            in >> tmp;

            if (0 != rank)
                sa.export_Solution(out);
        }
        else
        {//:even - faster
            if (rank & 1)
            {
                sa.export_Solution(out);
                in >> tmp;
            }
            else
            {
                in >> tmp;
                sa.export_Solution(out);
            }
        }
        return true;
    }
};
//__________________________________________________________________________________

#endif
//EOF!
